package top.codecrab.lock;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * synchronized和ReentrantLock的区别：
 * 一、sync是关键字，而ReentrantLock是java的一个类Api
 * <p>
 * 二、sync不用手动释放锁，其底层在方法执行完毕后或异常时，会释放锁，ReentrantLock则需要手动释放锁，否则会造成死锁
 * <p>
 * 三、sync不可被中断，ReentrantLock可以被中断，有两种方式：
 * 1、tryLock(timeout, unit)设置超时时间。2、lockInterruptibly()方法放在代码块中，调用Thread.interrupt()方法可中断
 * <p>
 * 四、sync是非公平锁，ReentrantLock可以选择公平和非公平：默认false非公平锁，true公平锁
 * <p>
 * 五、sync要么唤醒随机一个，要么唤醒全部，ReentrantLock可以指定多个条件Condition，实现分组唤醒，精确唤醒
 * <p>
 * 要求：A线程执行5次 --> B线程执行10次 --> C线程执行15次，循环打印十次
 *
 * @author codecrab
 * @since 2021年07月01日 17:32
 */
public class SyncAndLockDemo {
    public static void main(String[] args) {
        Resource resource = new Resource();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                resource.printA();
            }
        }, "A").start();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                resource.printB();
            }
        }, "B").start();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                resource.printC();
            }
        }, "C").start();
    }
}

class Resource {

    private final ReentrantLock lock = new ReentrantLock();
    private final Condition c1 = lock.newCondition();
    private final Condition c2 = lock.newCondition();
    private final Condition c3 = lock.newCondition();

    /**
     * A线程对应1，B:2，C:3
     */
    private int number = 1;

    public void printA() {
        lock.lock();
        try {
            while (1 != number) {
                c1.await();
            }

            for (int i = 1; i <= 5; i++) {
                System.out.println(Thread.currentThread().getName() + "\t" + i);
            }

            number = 2;
            c2.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void printB() {
        lock.lock();
        try {
            while (2 != number) {
                c2.await();
            }

            for (int i = 1; i <= 10; i++) {
                System.out.println(Thread.currentThread().getName() + "\t" + i);
            }

            number = 3;
            c3.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void printC() {
        lock.lock();
        try {
            while (3 != number) {
                c3.await();
            }

            for (int i = 1; i <= 15; i++) {
                System.out.println(Thread.currentThread().getName() + "\t" + i);
            }

            number = 1;
            c1.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}
